#include "../stdafx.h"
#include "bmplib.h"

static unsigned char * pBuffer = 0;
static unsigned char * pPointer = 0;

void set_bmp_data(unsigned char * pData)
{
	pPointer = pBuffer = pData;
}

void read_bmp_data(void * pDst,unsigned int size)
{
	if(pPointer)
	{
		memcpy(pDst, pPointer, size);
		pPointer += size;
	}
}

void read_bmp_header( _BMPHeader * pHeader)
{
	read_bmp_data(pHeader, sizeof(_BMPHeader));
}


void decompress_8Bit_RLE(unsigned char * & BmpData, long size, long width, long height, long pitch)
{
	unsigned char* p = BmpData;
	unsigned char* newBmp = new unsigned char[(width+pitch)*height];
	unsigned char* d = newBmp;
	unsigned char* destEnd = newBmp + (width+pitch)*height;
	long line = 0;
	while (BmpData - p < size && d < destEnd){
		if (*p == 0){
			++p;
			switch(*p){
			case 0:
				++p;
				++line;
				d = newBmp + (line*(width+pitch));
				break;
			case 1:
				delete [] BmpData;
				BmpData = newBmp;
				return;
			case 2:
				++p; d +=(unsigned char)*p;
				++p; d += ((unsigned char)*p)*(width+pitch);
				++p;
				break;
			default:
				{
					long count = (unsigned char)*p; ++p;
					long readAdditional = ((2-(count%2))%2);
					long i;

					for (i=0; i<count; ++i){
						*d = *p;
						++p;
						++d;
					}

					for (i=0; i<readAdditional; ++i)
						++p;
				}
			}
		}else{
			long count = (unsigned char)*p; ++p;
			unsigned char color = *p; ++p;
			for (int i=0; i<count; ++i){
				*d = color;
				++d;
			}
		}
	}

	delete [] BmpData;
	BmpData = newBmp;
}

void decompress_4Bit_RLE(unsigned char*& BmpData, long size, long width, long height, long pitch)
{
	long lineWidth = (width+1)/2+pitch;
	unsigned char* p = BmpData;
	unsigned char* newBmp = new unsigned char[lineWidth*height];
	unsigned char* d = newBmp;
	unsigned char* destEnd = newBmp + lineWidth*height;
	long line = 0;
	long shift = 4;

	while (BmpData - p < size && d < destEnd){
		if (*p == 0){
			++p;
			switch(*p){
			case 0:
				++p;
				++line;
				d = newBmp + (line*lineWidth);
				shift = 4;
				break;
			case 1:
				delete [] BmpData;
				BmpData = newBmp;
				return;
			case 2:
				{
					++p;
					long x = (unsigned char)*p; ++p;
					long y = (unsigned char)*p; ++p;
					d += x/2 + y*lineWidth;
					shift = x%2==0 ? 4 : 0;
				}
				break;
			default:
				{
					long count = (unsigned char)*p; ++p;
					long readAdditional = ((2-((count)%2))%2);
					long readShift = 4;
					long i;
					for (i=0; i<count; ++i){
						long color = (((unsigned char)*p) >> readShift) & 0x0f;
						readShift -= 4;
						if (readShift < 0){
							++*p;
							readShift = 4;
						}

						unsigned char mask = 0x0f << shift;
						*d = (unsigned char)((*d & (~mask)) | ((color << shift) & mask));
						shift -= 4;
						if (shift < 0){
							shift = 4;
							++d;
						}

					}
					for (i=0; i<readAdditional; ++i)
						++p;
				}
			}
		}else{
			long count = (unsigned char)*p; ++p;
			long color1 = (unsigned char)*p; color1 = color1 & 0x0f;
			long color2 = (unsigned char)*p; color2 = (color2 >> 4) & 0x0f;
			++p;

			for (long i=0; i<count; ++i){
				unsigned char mask = 0x0f << shift;
				unsigned char toSet = (unsigned char)((shift==0 ? color1 : color2) << shift);
				*d = (*d & (~mask)) | (toSet & mask);

				shift -= 4;
				if (shift < 0){
					shift = 4;
					++d;
				}
			}
		}
	}

	delete [] BmpData;
	BmpData = newBmp;
}

void read_bmp_image(unsigned char ** ppOut, bmp_image_info * pInfo)
{
	unsigned char * pData = new unsigned char[pInfo->dwOrgDataSize];
	memcpy(pData , pPointer, pInfo->dwOrgDataSize);
	switch(pInfo->dwDecompression)
	{
	case 1:
		decompress_8Bit_RLE(pData, pInfo->dwOrgDataSize, pInfo->dwWidth,  pInfo->dwHeight, pInfo->dwPitch);
		break;
	case 2:
		decompress_4Bit_RLE(pData, pInfo->dwOrgDataSize, pInfo->dwWidth,  pInfo->dwHeight, pInfo->dwPitch);
		break;
	}

	unsigned char * pRGBA=0;
	pInfo->dwByteNum = pInfo->dwWidth * pInfo->dwHeight * 4;
	pRGBA=new unsigned char[pInfo->dwByteNum];
	memset(pRGBA, 0, pInfo->dwByteNum);

	switch(pInfo->dwColorBits)
	{
	case 8:
		for(int row = 0, col = 0, w = 0, h = pInfo->dwHeight; row != h; ++row)
		{
			for(col = 0, w = pInfo->dwWidth; col != w; ++col)
			{
				memcpy(&pRGBA[((h - row - 1) * w + col) * 4], 
					&pInfo->pPalette[pData[row * w + col + row * pInfo->dwPitch] * 4], 3);
				pRGBA[((h - row - 1) * w + col) * 4 + 3] = 0xFF;

			}
		}
		break;
	case 16:
		{
			unsigned char * pDataOld = pData;
			unsigned char * pNewData = pRGBA;
			pRGBA += pInfo->dwWidth * pInfo->dwHeight;

			for (unsigned int y=0; y < pInfo->dwHeight; ++y)
			{
				pRGBA -= pInfo->dwWidth;
				memcpy(pRGBA, pData, pInfo->dwWidth*sizeof(short));
				pRGBA += pInfo->dwWidth;
				pData += pInfo->dwWidth;
				pData += pInfo->dwPitch;
			}
			pData = pDataOld;
			pRGBA = pNewData;
		}
		break;
	case 24:
		for(int row = 0, col = 0, w = 0,h = pInfo->dwHeight; row != h; ++row)
		{
			for(col = 0, w = pInfo->dwWidth; col != w; ++col)
			{
				memcpy(&pRGBA[((h - row - 1) * w + col) * 4 ], &pData[(row * w + col) * 3], 3);
				pRGBA[((h - row - 1) * w + col) * 4 + 3] = 0xFF;
			}
		}

		break;
	};
	delete [] pData;
	if(pInfo->pPalette)
	{
		delete []pInfo->pPalette;
		pInfo->pPalette = 0;
	}
	*ppOut = pRGBA;
}
